/*
* Copyright (c) 2016.2，华乘电气科技有限公司
* All rights reserved.
*
* 文件名称：Crypto.cpp
*
* 初始版本：1.0
* 作者：
* 创建日期：2016年2月26日
* 摘要：该文件主要是实现加密模块
*
*/
#include <QCryptographicHash>
#include "AesCrypto.h"
#include "DesCrypto.h"
#include "BlowFishCrypto.h"
#include "crypto.h"

typedef enum _CryptoMethod
{
    AES = 0x1 << 0,
    DES = 0x1 << 1,
    BLOWFISH = 0x1 << 2,
    BASE64 = 0x1 << 3
}CryptoMethod;


#define CRYPTO_METHOD (DES) //加密方法
/************************************************
 * 函数名    :makeKey
 * 输入参数  ：
 *      pchKey -- 密钥
 * 输出参数  ：NULL
 * 返回值   ：NULL
 * 功能     ：密钥生成算法
 ************************************************/
static QByteArray makeKey(const char* pchKey)
{
    QByteArray baKey = pchKey;

    baKey = QCryptographicHash::hash( baKey, QCryptographicHash::Md5 )+"PDSTARS";
    baKey = QCryptographicHash::hash( baKey, QCryptographicHash::Md4 );

    baKey = baKey.mid(0,8);

    return baKey;
}

/************************************************
 * 函数名    :getName
 * 输入参数  ：
 *      pchName -- 名称
 * 输出参数  ：NULL
 * 返回值   ：
 *      名称（中文解码后）
 * 功能     ：解密函数
 ************************************************/
static QString getName( const char* pchName );
class Crypto
{
public:

    /************************************************
     * 函数名    :Crypto
     * 输入参数  ：
     *      baKey -- 密钥
     *      method -- 加密算法
     * 输出参数  ：NULL
     * 返回值   ：NULL
     * 功能     ：构造函数
     ************************************************/
    Crypto( const char* baKey );

    /************************************************
     * 函数名    :enCrypt
     * 输入参数  ：
     *      fileInName -- 输入文件
     *      fileOutName -- 输出文件
     * 输出参数  ：NULL
     * 返回值   ：
     *      true -- 成功
     *      false -- 失败
     * 功能     ：加密函数
     ************************************************/
    bool enCrypt( const char* fileInName, const char* fileOutName  );


    /************************************************
     * 函数名    :enCrypt
     * 输入参数  ：
     *      data -- 输入数据
     * 输出参数  ：NULL
     * 返回值   ：
     *      加密后数据
     * 功能     ：加密函数
     ************************************************/
    QByteArray enCrypt( const QByteArray& data  );

    /************************************************
     * 函数名    :deCrypt
     * 输入参数  ：
     *      fileInName -- 输入文件
     *      fileOutName -- 输出文件
     * 输出参数  ：NULL
     * 返回值   ：
     *      true -- 成功
     *      false -- 失败
     * 功能     ：解密函数
     ************************************************/
    bool deCrypt( const char* fileInName, const char* fileOutName  );


    /************************************************
     * 函数名    :deCrypt
     * 输入参数  ：
     *      data -- 输入数据
     * 输出参数  ：NULL
     * 返回值   ：
     *      解密后数据
     * 功能     ：解密函数
     ************************************************/
    QByteArray deCrypt( const QByteArray& data  );
private:
    QByteArray m_baKey;
    unsigned m_method;
};
/************************************************
 * 函数名    :Crypto
 * 输入参数  ：
 *      baKey -- 密钥
 * 输出参数  ：NULL
 * 返回值   ：NULL
 * 功能     ：构造函数
 ************************************************/
Crypto::Crypto( const char* baKey )
{
    m_baKey = makeKey( baKey );
    m_method = CRYPTO_METHOD;
}

/************************************************
 * 函数名    :enCrypt
 * 输入参数  ：
 *      data -- 输入数据
 * 输出参数  ：NULL
 * 返回值   ：
 *      加密后数据
 * 功能     ：加密函数
 ************************************************/
QByteArray Crypto::enCrypt( const QByteArray& data  )
{
    QByteArray dataOut = qCompress( data );

    if( m_method & AES )
    {
        AesCrypto aes( m_baKey );
        dataOut = aes.enCrypt( dataOut );
    }
    if( m_method & DES )
    {
        DesCrypto des( m_baKey );
        dataOut = des.enCrypt( dataOut );
    }
    if( m_method & BLOWFISH )
    {
        BlowFishCrypto fish( m_baKey );
        dataOut = fish.enCrypt( dataOut );
    }
    if( m_method & BASE64 )
    {
        dataOut = dataOut.toBase64();
    }

    return dataOut;
}

/************************************************
 * 函数名    :enCrypt
 * 输入参数  ：
 *      fileInName -- 输入文件
 *      fileOutName -- 输出文件
 * 输出参数  ：NULL
 * 返回值   ：
 *      true -- 成功
 *      false -- 失败
 * 功能     ：加密函数
 ************************************************/
bool Crypto::enCrypt( const char* fileInName, const char* fileOutName  )
{
    bool bSuccess = true;
    QString strFileInName = getName( fileInName );
    QString strFileOutName = getName( fileOutName );

    QFile fileIn( strFileInName );
    QFile fileOut( strFileOutName );

    if( ( fileIn.open( QIODevice::ReadOnly ) && ( fileOut.open( QIODevice::ReadWrite ) ) ) )
    {
        QByteArray data = fileIn.readAll();

        data = qCompress( data );

        data = enCrypt( data );

        fileOut.resize(0);
        bSuccess = fileOut.write( data ) > 0;
    }
    else
    {
        bSuccess = false;
    }

    return bSuccess;
}

/************************************************
 * 函数名    :deCrypt
 * 输入参数  ：
 *      data -- 输入数据
 * 输出参数  ：NULL
 * 返回值   ：
 *      解密后数据
 * 功能     ：解密函数
 ************************************************/
QByteArray Crypto::deCrypt( const QByteArray& data  )
{
    QByteArray dataOut = data;

    if( m_method & BASE64 )
    {
        dataOut = dataOut.fromBase64( dataOut );
    }

    if( m_method & BLOWFISH )
    {
        BlowFishCrypto fish( m_baKey );
        dataOut = fish.deCrypt( dataOut );
    }
    if( m_method & DES )
    {
        DesCrypto des( m_baKey );
        dataOut = des.deCrypt( dataOut );
    }
    if( m_method & AES )
    {
        AesCrypto aes( m_baKey );
        dataOut = aes.deCrypt( dataOut );
    }

    dataOut = qUncompress( dataOut );

    return dataOut;
}
/************************************************
 * 函数名    :getName
 * 输入参数  ：
 *      pchName -- 名称
 * 输出参数  ：NULL
 * 返回值   ：
 *      名称（中文解码后）
 * 功能     ：解密函数
 ************************************************/
QString getName( const char* pchName )
{
    QTextCodec*codec=QTextCodec::codecForName("utf8");

    return codec->toUnicode( pchName );
}

/************************************************
 * 函数名    :deCrypt
 * 输入参数  ：
 *      fileInName -- 输入文件
 *      fileOutName -- 输出文件
 * 输出参数  ：NULL
 * 返回值   ：
 *      true -- 成功
 *      false -- 失败
 * 功能     ：解密函数
 ************************************************/
bool Crypto::deCrypt( const char* fileInName, const char* fileOutName  )
{
    bool bSuccess = true;
    QString strFileInName = getName( fileInName );
    QString strFileOutName = getName( fileOutName );

    QFile fileIn( strFileInName );
    QFile fileOut( strFileOutName );

    if( ( fileIn.open( QIODevice::ReadOnly ) && ( fileOut.open( QIODevice::ReadWrite ) ) ) )
    {
        QByteArray data = fileIn.readAll();

        data = deCrypt( data );

        data = qUncompress( data );

        fileOut.resize(0);
        bSuccess = fileOut.write( data ) > 0;
    }
    else
    {
        bSuccess = false;
    }

    return bSuccess;
}

static Crypto* s_pCrypto = NULL;
static QMutex s_mutex;
/************************************************
 * 函数名    :initCrypto
 * 输入参数  ：
 *      baKey -- 密钥
 * 输出参数  ：NULL
 * 返回值   ：NULL
 * 功能     ：初始化函数
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" void initCrypto( char* pchKey )
#else
extern "C" __declspec(dllexport) void __stdcall initCrypto( char* pchKey )
#endif
{
    s_mutex.lock();
    if( NULL != s_pCrypto )
    {
        delete s_pCrypto;
        s_pCrypto = NULL;
    }

    s_pCrypto = new Crypto( pchKey );
    s_mutex.unlock();
}

/************************************************
 * 函数名    :closeCrypto
 * 输入参数  ：NULL
 * 输出参数  ：NULL
 * 返回值   ：NULL
 * 功能     ：关闭加密模块函数
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" void closeCrypto( void )
#else
extern "C" __declspec(dllexport) void __stdcall closeCrypto( void )
#endif
{
    s_mutex.lock();
    if( NULL != s_pCrypto )
    {
        delete s_pCrypto;
        s_pCrypto = NULL;
    }

    s_mutex.unlock();
}

/************************************************
 * 函数名    :enCryptFileToFile
 * 输入参数  ：
 *      fileInName -- 输入文件
 *      fileOutName -- 输出文件
 * 输出参数  ：NULL
 * 返回值   ：
 *      true -- 成功
 *      false -- 失败
 * 功能     ：加密函数
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" bool enCryptFileToFile( const char* fileInName, const char* fileOutName  )
#else
extern "C" __declspec(dllexport) bool __stdcall enCryptFileToFile( const char* fileInName, const char* fileOutName  )
#endif
{
    bool bSucceed = false;

    s_mutex.lock();
    if( NULL != s_pCrypto )
    {
        bSucceed = s_pCrypto->enCrypt( fileInName, fileOutName );
    }
    s_mutex.unlock();

    return bSucceed;
}


/************************************************
 * 函数名    :enCrypt
 * 输入参数  ：
 *      data -- 被加密数据
 * 输出参数  ：NULL
 * 返回值   ：
 *      加密后数据
 * 功能     :加密
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" QByteArray enCrypt( const QByteArray& data )
#else
extern "C" __declspec(dllexport) QByteArray enCrypt( const QByteArray& data )
#endif
{
    QByteArray dataOut;

    s_mutex.lock();
    if( NULL != s_pCrypto )
    {
        dataOut = s_pCrypto->enCrypt( data );
    }
    s_mutex.unlock();

    return dataOut;
}
/************************************************
 * 函数名    :enCrypt
 * 输入参数  ：
 *      pchData -- 输入数据
 *      uLen -- 长度
 *      fileOutName -- 输出文件
 * 输出参数  ：NULL
 * 返回值   ：
 *      true -- 成功
 *      false -- 失败
 * 功能     ：加密函数
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" bool enCryptByteToFile( const char* pchData, unsigned long uLen, const char* fileOutName  )
#else
extern "C" __declspec(dllexport) bool __stdcall enCryptByteToFile( const char* pchData, unsigned long uLen, const char* fileOutName  )
#endif
{
    QString strFileOutName = getName( fileOutName );

    QFile file( strFileOutName );

    bool bSucceed = true;

    s_mutex.lock();
    if( ( NULL == s_pCrypto ) || ( NULL == pchData ) || ( 0 == uLen ) || ( !file.open( QIODevice::ReadWrite ) ) )
    {
        bSucceed = false;
    }
    else
    {
        file.resize(0);
        QByteArray data = QByteArray::fromRawData( pchData, uLen );

        QByteArray dataOut = s_pCrypto->enCrypt( data );

        file.write(dataOut);
    }
    s_mutex.unlock();

    return bSucceed;
}

/************************************************
 * 函数名    :deCryptFileToFile
 * 输入参数  ：
 *      fileInName -- 输入文件
 *      fileOutName -- 输出文件
 * 输出参数  ：NULL
 * 返回值   ：
 *      true -- 成功
 *      false -- 失败
 * 功能     ：解密函数
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" bool deCryptFileToFile( const char* fileInName, const char* fileOutName  )
#else
extern "C" __declspec(dllexport) bool __stdcall  deCryptFileToFile( const char* fileInName, const char* fileOutName  )
#endif
{
    bool bSucceed = false;

    s_mutex.lock();
    if( ( NULL == s_pCrypto ) ||  NULL != s_pCrypto )
    {
        bSucceed = s_pCrypto->deCrypt( fileInName, fileOutName );
    }
    s_mutex.unlock();

    return bSucceed;
}

/************************************************
 * 函数名    :getDecryptByteLen:注：后续必须加getDecryptByte(char* pchOut);
 * 输入参数  ：
 *      fileInName -- 输入文件名
 * 输出参数  ：NULL
 * 返回值   ：
 *      解密后数据长度
 * 功能     ：获取解密文件名长
 ************************************************/
static QByteArray* s_pdataOut = NULL;

#ifdef Q_OS_LINUX
extern "C" unsigned long getDecryptByteLen( const char* fileInName )
#else
extern "C" __declspec(dllexport) unsigned long __stdcall getDecryptByteLen( const char* fileInName )
#endif
{
    QString strFileInName = getName( fileInName );
    QFile file( strFileInName );

    unsigned long iResult = -1;

    s_mutex.lock();

    if( ( NULL == s_pCrypto ) ||  ( NULL == fileInName ) || ( !file.open( QIODevice::ReadOnly ) ) )
    {
        iResult = -1;
    }

    QByteArray data = file.readAll();
    if( NULL != s_pdataOut )
    {
        delete s_pdataOut;
        s_pdataOut = NULL;
    }
    s_pdataOut = new QByteArray;

    *s_pdataOut = s_pCrypto->deCrypt( data );

    iResult = s_pdataOut->count();

    s_mutex.unlock();

    return iResult;
}


/************************************************
 * 函数名    :getDecryptByte
 * 输入参数  ：
 *      pchOut -- 存放数据缓存
 * 输出参数  ：NULL
 * 返回值   ：
 *      true -- 成功
 *      false -- 失败
 * 功能     ：获取解密数据
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" bool getDecryptByte( char* pchOut )
#else
extern "C" __declspec(dllexport) bool __stdcall getDecryptByte( char* pchOut )
#endif
{
    bool bSucceed = false;

    s_mutex.lock();
    if( NULL == pchOut )
    {
        bSucceed = false;
    }
    else
    {
        memcpy( pchOut, s_pdataOut->data(), s_pdataOut->count() );
        bSucceed = true;
        delete s_pdataOut;
        s_pdataOut = NULL;
    }
    s_mutex.unlock();

    return bSucceed;
}


/************************************************
 * 函数名    :deCrypt
 * 输入参数  ：
 *      data -- 被解密数据
 * 输出参数  ：NULL
 * 返回值   ：
 *      解密后数据
 * 功能     :解密
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" QByteArray deCrypt( const QByteArray& data )
#else
extern "C" __declspec(dllexport) QByteArray deCrypt( const QByteArray& data )
#endif
{
    QByteArray dataOut;

    s_mutex.lock();
    if( NULL != s_pCrypto )
    {
        dataOut = s_pCrypto->deCrypt( data );
    }
    s_mutex.unlock();

    return dataOut;
}

/************************************************
 * 函数名    :GetMD5EncryptLen:注：后续必须加MD5Encrypt(char* pchOut, char* strEncrypted)
 * 输入参数  ：
 *      strToEncrypt -- 需要加密的char* 指针
 * 返回值   ：
 *      加密后的数据长度
 * 功能     ：获取MD5加密数据长度
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" int GetMD5EncryptLen(char* strToEncrypt)
#else
extern "C" __declspec(dllexport) int __stdcall GetMD5EncryptLen(char* strToEncrypt)
#endif
{
    QByteArray enStr;
    QByteArray byteArrayToEncrypt(strToEncrypt);
    enStr = QCryptographicHash::hash(byteArrayToEncrypt, QCryptographicHash::Md5);

    return strlen(enStr.data());
}

/************************************************
 * 函数名    :MD5Encrypt
 * 输入参数  ：
 *      strToEncrypt -- 需要加密的char* 数组
 * 输出参数  ：
 *      strEncrypted -- 加密后的char* 数组
 * 返回值   ：
 *      加密后的数据
 * 功能     :MD5加密
 ************************************************/
#ifdef Q_OS_LINUX
extern "C" int MD5Encrypt(char* strToEncrypt, char* strEncrypted)
#else
extern "C" __declspec(dllexport) int __stdcall MD5Encrypt(char* strToEncrypt, char* strEncrypted)
#endif
{
    QByteArray enStr;
    QByteArray byteArrayToEncrypt(strToEncrypt);
    enStr = QCryptographicHash::hash(byteArrayToEncrypt, QCryptographicHash::Md5);

    int iLen = strlen(enStr.data());
    strncpy(strEncrypted, enStr.data(), iLen);
    return 0;
}
